package wci.frontend.pascal.parsers;

import java.util.ArrayList;
import java.util.EnumSet;

import wci.frontend.*;
import wci.frontend.pascal.*;
import wci.intermediate.*;
import wci.intermediate.symtabimpl.*;
import wci.intermediate.icodeimpl.*;
import wci.intermediate.typeimpl.*;

import static wci.frontend.pascal.PascalTokenType.*;
import static wci.frontend.pascal.PascalErrorCode.*;
import static wci.intermediate.symtabimpl.SymTabKeyImpl.*;
import static wci.intermediate.symtabimpl.DefinitionImpl.*;
import static wci.intermediate.symtabimpl.RoutineCodeImpl.*;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.*;
import static wci.intermediate.icodeimpl.ICodeKeyImpl.*;
import static wci.intermediate.typeimpl.TypeFormImpl.*;

/**
 * <h1>CallParser</h1>
 * <p>解析一个程式（过程或函数）调用</p>
 * <p>Copyright (c) 2009 by Ronald Mak</p>
 * <p>For instructional purposes only.  No warranties.</p>
 */
public class CallParser extends StatementParser
{
    public CallParser(PascalParserTD parent)
    {
        super(parent);
    }
    public ICodeNode parse(Token token)
        throws Exception
    {
    	//视情况调用申明解析或标准解析
        SymTabEntry pfId = symTabStack.lookup(token.getText().toLowerCase());
        RoutineCode routineCode = (RoutineCode) pfId.getAttribute(ROUTINE_CODE);
        StatementParser callParser = (routineCode == DECLARED) ||
                                     (routineCode == FORWARD)
                                         ? new CallDeclaredParser(this)
                                         : new CallStandardParser(this);

        return callParser.parse(token);
    }

    //逗号处的同步集，隔开实参的
    private static final EnumSet<PascalTokenType> COMMA_SET =
        ExpressionParser.EXPR_START_SET.clone();
    static {
        COMMA_SET.add(COMMA);
        COMMA_SET.add(RIGHT_PAREN);
    };

    /**
     * 解析程式调用实参
     * @param token 当前token
     * @param pfId 程式名称符号表项
     * @param isDeclared true意味着是一个申明程式
     * @param isReadReadln true意味着是read/readln.
     * @param isWriteWriteln true意味着是write/writeln
     * @return PARAMETERS节点，null表示无参数
     * @throws Exception
     */
    protected ICodeNode parseActualParameters(Token token, SymTabEntry pfId,
                                              boolean isDeclared,
                                              boolean isReadReadln,
                                              boolean isWriteWriteln)
        throws Exception
    {
        ExpressionParser expressionParser = new ExpressionParser(this);
        ICodeNode parmsNode = ICodeFactory.createICodeNode(PARAMETERS);
        ArrayList<SymTabEntry> formalParms = null;
        int parmCount = 0;
        int parmIndex = -1;
        //如果是申明程式，很容易取得参数个数
        if (isDeclared) {
            formalParms =
                (ArrayList<SymTabEntry>) pfId.getAttribute(ROUTINE_PARMS);
            parmCount = formalParms != null ? formalParms.size() : 0;
        }

        if (token.getType() != LEFT_PAREN) {
            if (parmCount != 0) {
                errorHandler.flag(token, WRONG_NUMBER_OF_PARMS, this);
            }

            return null;
        }

        token = nextToken();  // 跳过左口号XX(y,z)

        // 遍历每一个实参
        while (token.getType() != RIGHT_PAREN) {
            ICodeNode actualNode = expressionParser.parse(token);

            //如果是申明程式，需要检查形参和实参的匹配
            if (isDeclared) {
                if (++parmIndex < parmCount) {
                    SymTabEntry formalId = formalParms.get(parmIndex);
                    checkActualParameter(token, formalId, actualNode);
                }
                else if (parmIndex == parmCount) {
                    errorHandler.flag(token, WRONG_NUMBER_OF_PARMS, this);
                }
            }

            // 对于read/readln来说，实参必须为标量/布尔还有整数子界
            else if (isReadReadln) {
                TypeSpec type = actualNode.getTypeSpec();
                TypeForm form = type.getForm();

                if (! (   (actualNode.getType() == ICodeNodeTypeImpl.VARIABLE)
                       && ( (form == SCALAR) ||
                            (type == Predefined.booleanType) ||
                            ( (form == SUBRANGE) &&
                              (type.baseType() == Predefined.integerType) ) )
                      )
                   )
                {
                    errorHandler.flag(token, INVALID_VAR_PARM, this);
                }
            }

            // 对于write/writeln，除了参数要求外，还有可选的宽度和精度
            else if (isWriteWriteln) {

                // Create a WRITE_PARM node which adopts the expression node.
                ICodeNode exprNode = actualNode;
                actualNode = ICodeFactory.createICodeNode(WRITE_PARM);
                actualNode.addChild(exprNode);

                TypeSpec type = exprNode.getTypeSpec().baseType();
                TypeForm form = type.getForm();

                if (! ( (form == SCALAR) || (type == Predefined.booleanType) ||
                        (type.isPascalString())
                      )
                   )
                {
                    errorHandler.flag(token, INCOMPATIBLE_TYPES, this);
                }

                //可选的宽度
                token = currentToken();
                actualNode.addChild(parseWriteSpec(token));

                //可选的精度
                token = currentToken();
                actualNode.addChild(parseWriteSpec(token));
            }

            parmsNode.addChild(actualNode);
            token = synchronize(COMMA_SET);
            TokenType tokenType = token.getType();

            // 是否分割实参的逗号？
            if (tokenType == COMMA) {
                token = nextToken();
            }
            else if (ExpressionParser.EXPR_START_SET.contains(tokenType)) {
                errorHandler.flag(token, MISSING_COMMA, this);
            }
            else if (tokenType != RIGHT_PAREN) {
                token = synchronize(ExpressionParser.EXPR_START_SET);
            }
        }

        token = nextToken();  // 跳过右括号

        if ((parmsNode.getChildren().size() == 0) ||
            (isDeclared && (parmIndex != parmCount-1)))
        {
            errorHandler.flag(token, WRONG_NUMBER_OF_PARMS, this);
        }

        return parmsNode;
    }

    //检测实参/形参的匹配
    private void checkActualParameter(Token token, SymTabEntry formalId,
                                      ICodeNode actualNode)
    {
        Definition formalDefn = formalId.getDefinition();
        TypeSpec formalType = formalId.getTypeSpec();
        TypeSpec actualType = actualNode.getTypeSpec();

        // VAR参数(引用传递): 实参类型必须与形参类型保持一致
        if (formalDefn == VAR_PARM) {
            if ((actualNode.getType() != ICodeNodeTypeImpl.VARIABLE) ||
                (actualType != formalType))
            {
                errorHandler.flag(token, INVALID_VAR_PARM, this);
            }
        }

        // VALUE参数(值传递): 检查实参能否与形参赋值兼容？
        else if (!TypeChecker.areAssignmentCompatible(formalType, actualType)) {
            errorHandler.flag(token, INCOMPATIBLE_TYPES, this);
        }
    }

    //解析write/writeln实参额外的宽度/精度 比如 writeln(xx:50:2)
    private ICodeNode parseWriteSpec(Token token)
        throws Exception
    {
        if (token.getType() == COLON) {
            token = nextToken();  // 跳过冒号

            ExpressionParser expressionParser = new ExpressionParser(this);
            ICodeNode specNode = expressionParser.parse(token);
            //必须是整数
            if (specNode.getType() == INTEGER_CONSTANT) {
                return specNode;
            }
            else {
                errorHandler.flag(token, INVALID_NUMBER, this);
                return null;
            }
        }
        else {
            return null;
        }
    }
}
